/**
 * Copyright (C) 2015-2018 IQIYI
 * All rights reserved.
 *
 * Author     : 王海斌
 * E-mail     : wanghaibin@qiyi.com
 * Version    :
 * Date       : Mon 17 Jun 2019 11:54:12 AM CST
 * Destription:  测试mmap 相关内容
 *
 * test_direct_mmap_to_struct
 *  验证确定可以通过mmap将地址直接映射为结构体对象，
 *  然后通过结构体指针修改内容后磁盘文件内容直接被修改
 */

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>


void test_private_mmap(const char * file) {

    int fd, nread, i;
    struct stat sb;
    char *mapped;
    /* 打开文件 */
    if ((fd = open (file, O_RDWR)) < 0)
    {
        perror ("open");
    }
    /* 获取文件的属性 */
    if ((fstat (fd, &sb)) == -1)
    {
        perror ("fstat");
    }
    /* 将文件映射至进程的地址空间 */
    //if ((mapped = (char *) mmap (NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == (void *) -1)
    if ((mapped = (char *) mmap (NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) == (void *) -1)
    {
        perror ("mmap");
    }
    /* 映射完后, 关闭文件也可以操纵内存 */


    printf ("get mapped value %s\n", mapped);
    printf("you have 10 second to modify file\n");
    sleep(10);
    printf ("get mapped value2 %s", mapped);
    sleep(10);
    /* 修改一个字符,同步到磁盘文件 */
    //mapped[0] = 'm';
    close (fd);
    if ((msync ((void *) mapped, sb.st_size, MS_SYNC)) == -1)
    {
        perror ("msync");
    }
    /* 释放存储映射区 */
    if ((munmap ((void *) mapped, sb.st_size)) == -1)
    {
        perror ("munmap");
    }
    printf("AAAAAAAAAAAAA \r\n");
    sleep(20);
}

typedef struct test_mmap_struct {
    int value1;
    int value2;
    long long value3;
    test_mmap_struct() {
        value1 = 0;
        value2 = 0;
        value3 = 3;
    }
}test_mmap_struct;

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

// 测试直接将文件mmap到结构体，然后修改结构体内容后是否可以直接刷新到磁盘
// 测试结果是可以直接刷新到磁盘
void test_direct_mmap_to_struct(const char * path) {
    int fd = open(path, O_RDWR|O_CREAT, 0644);
    if (fd == -1)
        handle_error("open");

    struct stat sb;
    if (fstat(fd, &sb) == -1)           /* To obtain file size */
        handle_error("fstat");
    size_t length = sizeof(test_mmap_struct);
    if(sb.st_size<=0) {
        printf("first init \n");
        test_mmap_struct tmp;
        if(write(fd, &tmp, sizeof(tmp))!=length) {
            handle_error("write");
        } else {
            fstat(fd, &sb);
        }
    }

    bool breset = false;;
    if(sb.st_size<length) {
        ftruncate(fd, length);
        printf("truncate filesize=%zd, to struct length =%zd\n", sb.st_size, length);
        breset = true;
    }
    void * addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED)
        handle_error("mmap");
    test_mmap_struct * test_struct = (test_mmap_struct*)addr;
    test_struct->value1++;
    //if(breset) { // 重新定义长度的时候要重置新增的变量内容
    //    test_struct->value4 = 4;
    //}
    if (msync(addr, length, MS_SYNC)<0) {
        handle_error("msync");
    }
    if (munmap(addr, length)<0) {
        handle_error("munmap");
    }
    close(fd);
}

int  main (int argc, char **argv) {
    if ( argc <= 1 ) {
        printf("%s: Need file path! \n",argv[0]);
        exit(-1);
    }
//    test_private_mmap(argv[1]);

    //sleep(2);
    test_direct_mmap_to_struct(argv[1]);
    return 0;
}
